package org.flowable.pz.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.*;
import org.flowable.engine.task.Comment;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.pz.anno.FlowableAuth;
import org.flowable.pz.constant.FlowableConstant;
import org.flowable.pz.entity.dto.FlowableProcessDto;
import org.flowable.pz.entity.dto.FlowablePageDto;
import org.flowable.pz.entity.dto.FlowableTaskDto;
import org.flowable.pz.entity.vo.FlowableProcessQueryVo;
import org.flowable.pz.entity.vo.FlowablePageQueryVo;
import org.flowable.pz.entity.vo.FlowableTaskQueryVo;
import org.flowable.pz.enums.FlowableStatusEnum;
import org.flowable.pz.exception.FlowableCustomException;
import org.flowable.pz.expand.FlowableUserExpand;
import org.flowable.pz.service.FlowableProcess;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 流程实例相关操作
 * */
@Service
@Slf4j
@Validated
public class FlowableProcessImpl implements FlowableProcess {

    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final HistoryService historyService;

    private final RepositoryService repositoryService;

    private final ProcessEngineConfiguration processEngineConfiguration;
    /**
     * 用户信息
     * */
    private final FlowableUserExpand flowableUserExpand;

    public FlowableProcessImpl(RuntimeService runtimeService,
                               TaskService taskService,
                               HistoryService historyService,
                               FlowableUserExpand flowableUserExpand,
                               RepositoryService repositoryService,
                               ProcessEngineConfiguration processEngineConfiguration){
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.historyService = historyService;
        this.flowableUserExpand = flowableUserExpand;
        this.repositoryService = repositoryService;
        this.processEngineConfiguration = processEngineConfiguration;
    }



    @Override
    @FlowableAuth
    public String startProcess(String processDefinitionKey, Map<String, Object> variables) {

        /*
        * 设置用户
        * 通过 variable 设置的用户，只在 BPMN 中需要时才设置 initiator 变量，也就是理解为我们流程中需要自己设置，约定这几个用户
        * */
        //variables.put("initiator", flowableUserExtend.getCurrentUserId());
        // 业务自定义变量（根据实际需要）
//        variables.put("applyUser", userId);      // 申请人
//        variables.put("startUser", userId);      // 启动用户
//        variables.put("businessUser", userId);   // 业务用户

        /*
         * 调用 flowable 功能发起流程实例
         */
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
        log.info("发起流程 {} 成功：{}", processInstance.getProcessDefinitionName(), processInstance.getId());
        return processInstance.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelProcess(String processInstanceId, String cancelComment) {

        validateRunTimeProcessInstance(processInstanceId);

        //修改业务状态
        runtimeService.updateBusinessStatus(processInstanceId, String.valueOf(FlowableStatusEnum.DELETED.getStatus()));

        /**
         * 直接执行删除操作，这个操作内部实际操作：
         * update ACT_HI_ACTINST SET REV_ = ?, PROC_DEF_ID_ = ?, END_TIME_ = ?, DURATION_ = ?, DELETE_REASON_ = ? where ID_ = ? and REV_ = ?
         * update ACT_HI_PROCINST SET REV_ = ?, END_TIME_ = ?, DURATION_ = ?, DELETE_REASON_ = ? where ID_ = ? and REV_ = ?
         * update ACT_HI_TASKINST SET REV_ = ?, END_TIME_ = ?, DURATION_ = ?, DELETE_REASON_ = ?, LAST_UPDATED_TIME_ = ? where ID_ = ? and REV_ = ?
         * update ACT_RU_EXECUTION SET REV_ = ?, IS_ACTIVE_ = ?, TASK_COUNT_ = ? where ID_ = ? and REV_ = ?
         * update ACT_RU_EXECUTION SET REV_ = ?, IS_ACTIVE_ = ? where ID_ = ? and REV_ = ?
         *
         * delete from ACT_RU_VARIABLE where EXECUTION_ID_ = ?
         * delete from ACT_RU_IDENTITYLINK where PROC_INST_ID_ = ?
         * delete from ACT_RU_TASK where ID_ = ? and REV_ = ?
         * delete from ACT_RU_TASK where EXECUTION_ID_ = ?
         * delete from ACT_RU_ACTINST where PROC_INST_ID_ = ?
         * delete from ACT_RU_ACTINST where PROC_INST_ID_ = ?
         * delete from ACT_RU_EXECUTION where ID_ = ? and REV_ = ?
         *
         * 所以总结下来就是：
         *      ACT_HI_* 表留，更新上 delete_reason_ 字段
         *      ACT_RU_* 表删
         */
        runtimeService.deleteProcessInstance(processInstanceId, cancelComment);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopProcess(String processInstanceId, String stopComment) {

        //验证流程实例
        ProcessInstance instance = validateRunTimeProcessInstance(processInstanceId);

        //获取结束节点
        String endEvent = findSuitableEndEvent(instance.getProcessDefinitionId());

        //设置流程结束备注
        taskService.addComment(null, processInstanceId, FlowableConstant.COMMENT_TYPE_STOP, stopComment);

        //设置业务状态
        runtimeService.updateBusinessStatus(processInstanceId, String.valueOf(FlowableStatusEnum.TERMINATED.getStatus()));

        //跳转到结束节点
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(processInstanceId)
                .moveActivityIdsToSingleActivityId(runtimeService.getActiveActivityIds(processInstanceId), endEvent)  // 跳转到结束节点
                .changeState();
    }

    @Override
    public void suspendProcess(String processInstanceId) {

        //验证流程实例
        if(processInstanceId == null){
            throw new FlowableCustomException("流程 id 参数不能为 null");
        }

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if(processInstance == null){
            throw new FlowableCustomException("流程实例不存在或已结束");
        }

        //流程挂起 or 流程启动
        if(processInstance.isSuspended()) {
            runtimeService.activateProcessInstanceById(processInstanceId);
        }else{
            runtimeService.suspendProcessInstanceById(processInstanceId);
        }


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @FlowableAuth
    public void completeProcess(String taskId, String comment, Map<String, Object> variables){

        //todo 理论上，这里需要判断一下当前人是否是任务指派人，才能进行处理。

        //因为添加评论需要关联流程实例，据说是为了避免孤儿评论，所以需要传一个流程实例 id，这里索性查询下当前任务，做个判断
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .active()
                .singleResult();
        if(task == null){
            throw new FlowableCustomException("获取任务信息异常！！！");
        }

        //添加备注
        if(comment != null) {
            taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        }

        //审批任务，
        taskService.complete(taskId, variables);
    }

    @Override
    public FlowablePageDto<FlowableProcessDto> processList(FlowableProcessQueryVo queryVo) {

        //组装查询条件实例
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();

        return operateProcessList(processInstanceQuery, historicProcessInstanceQuery, queryVo);
    }

    /**
     * 流程抽出公共方法
     * */
    private FlowablePageDto<FlowableProcessDto> operateProcessList(ProcessInstanceQuery processInstanceQuery, HistoricProcessInstanceQuery historicProcessInstanceQuery, FlowableProcessQueryVo queryVo){
        //没有任何条件
        if(queryVo == null){
            queryVo = FlowablePageQueryVo.base(FlowableProcessQueryVo.class);
        }

        processInstanceQuery.orderByStartTime().desc();
        historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc();

        //流程实例 id
        if(queryVo.getProcessInstanceId() != null){
            processInstanceQuery.processInstanceId(queryVo.getProcessInstanceId());
            historicProcessInstanceQuery.processInstanceId(queryVo.getProcessInstanceId());
        }

        //流程定义 key
        if(queryVo.getProcessDefineKey() != null){
            processInstanceQuery.processDefinitionKey(queryVo.getProcessDefineKey());
            historicProcessInstanceQuery.processDefinitionKey(queryVo.getProcessDefineKey());
        }

        //查询范围
        int startIndex = (queryVo.getPageIndex() - 1) * queryVo.getPageSize();
        int endIndex = queryVo.getPageIndex() * queryVo.getPageSize();

        //返回结果
        long count = 0;
        List<FlowableProcessDto> resList = new ArrayList<>();

        //状态
        if(queryVo.getStatus() != null){
            FlowableStatusEnum flowStatus = FlowableStatusEnum.getByCode(queryVo.getStatus());
            switch(flowStatus){
                case ACTIVE:
                    //激活
                    processInstanceQuery.active();
                    count = processInstanceQuery.count();
                    resList = processInstanceQuery.listPage(startIndex, endIndex).stream().map(pi -> FlowableProcessDto.builder()
                            .processInstanceId(pi.getProcessInstanceId())
                            .processDefinitionName(pi.getProcessDefinitionName())
                            .startTime(pi.getStartTime())
                            .starter(pi.getStartUserId())
                            .status(FlowableStatusEnum.ACTIVE.getStatus())
                            .build()
                    ).collect(Collectors.toList());
                    break;
                case SUSPENDED:
                    //挂起
                    processInstanceQuery.suspended();
                    count = processInstanceQuery.count();
                    resList = processInstanceQuery.listPage(startIndex, endIndex).stream().map(pi -> FlowableProcessDto.builder()
                            .processInstanceId(pi.getProcessInstanceId())
                            .processDefinitionName(pi.getProcessDefinitionName())
                            .startTime(pi.getStartTime())
                            .starter(pi.getStartUserId())
                            .status(FlowableStatusEnum.SUSPENDED.getStatus())
                            .build()
                    ).collect(Collectors.toList());
                    break;
                case COMPLETED:
                    historicProcessInstanceQuery.finished();
                    count = historicProcessInstanceQuery.count();
                    resList = historicProcessInstanceQuery.listPage(startIndex, endIndex).stream().map(hi -> FlowableProcessDto.builder()
                            .processInstanceId(hi.getId())
                            .processDefinitionName(hi.getProcessDefinitionName())
                            .startTime(hi.getStartTime())
                            .endTime(hi.getEndTime())
                            .starter(hi.getStartUserId())
                            .status(FlowableStatusEnum.COMPLETED.getStatus())
                            .build()
                    ).collect(Collectors.toList());
                    break;
                default:
                    //todo 其他流程状态，暂时抛个异常处理
            }
        }else{
            //此时查询全部
            count = historicProcessInstanceQuery.count();
            List<HistoricProcessInstance> tempLists = historicProcessInstanceQuery.listPage(startIndex, endIndex);
            Map<String, ProcessInstance> tempMap;
            if(tempLists != null && !tempLists.isEmpty()) {
                tempMap = processInstanceQuery.processInstanceIds(tempLists.stream().map(HistoricProcessInstance::getId).collect(Collectors.toSet()))
                        .list()
                        .stream()
                        .collect(Collectors.toMap(
                                Execution::getProcessInstanceId,
                                e -> e,
                                (processInstance, processInstance2) -> processInstance
                        ));
            } else {
                tempMap = new HashMap<>();
            }

            assert tempLists != null;
            resList = tempLists.stream().map(hi -> FlowableProcessDto.builder()
                    .processInstanceId(hi.getId())
                    .processDefinitionName(hi.getProcessDefinitionName())
                    .startTime(hi.getStartTime())
                    .endTime(hi.getEndTime())
                    .starter(hi.getStartUserId())
                    .status(operateStatusByRuntimeInstance(hi, tempMap.get(hi.getId())))
                    .build()
            ).collect(Collectors.toList());
        }

        //补充一下当前节点，处理人
        resList.stream().filter(e -> e.getStatus().equals( FlowableStatusEnum.ACTIVE.getStatus() ))
                .forEach(flowableInstanceDto -> {
                    List<ActivityInstance> list = runtimeService.createActivityInstanceQuery()
                            .processInstanceId(flowableInstanceDto.getProcessInstanceId())
                            .unfinished()
                            .activityType(FlowableConstant.ACT_TYPE_USER_TASK)
                            .list();
                    flowableInstanceDto.setCurrentTaskList( list.stream().map(instance -> FlowableTaskDto.builder().name(instance.getActivityName()).userId(instance.getAssignee()).build()).collect(Collectors.toList()) );
                    log.info("当前节点：{}", list);
                });


        return new FlowablePageDto<>(queryVo.getPageIndex(), queryVo.getPageSize(), (int)count, resList);
    }

    /**
     * @param hpi 表示一个历史流程
     * @param pi 表示一个执行中的流程实例
     * 根据执行中的流程实例，判断流程状态
     * */
    private int operateStatusByRuntimeInstance(HistoricProcessInstance hpi, ProcessInstance pi){
        if(hpi == null){
            throw new FlowableCustomException("流程不存在！");
        }
        //执行中的流程，挂起或者激活状态
        if(pi != null){
            return pi.isSuspended()?FlowableStatusEnum.SUSPENDED.getStatus():FlowableStatusEnum.ACTIVE.getStatus();
        }

        //已束的流程，分为正常结束，已删除，已终止
        if(hpi.getDeleteReason() != null){
            /*
             * historicProcessInstanceQuery.deleted().listPage(startIndex, endIndex)
             * 这个 deleted(） 就是根据 delete_reason 来判断的
             * 或者根据 business_status 来判断也可以
             */
            return FlowableStatusEnum.DELETED.getStatus();
        }

        //通过业务属性判断终止状态，删除的流程也可以这么判断
        if(hpi.getBusinessStatus() != null && hpi.getBusinessStatus().equals( String.valueOf( FlowableStatusEnum.TERMINATED.getStatus() ) )) {
            return FlowableStatusEnum.TERMINATED.getStatus();
        }
        return FlowableStatusEnum.COMPLETED.getStatus();

    }

    @Override
    public FlowablePageDto<FlowableProcessDto> myProcessList(FlowableProcessQueryVo queryVo) {
        log.info("查询流程列表，当前用户：{}", flowableUserExpand.getCurrentUserId());

        //组装查询条件实例
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery().startedBy(flowableUserExpand.getCurrentUserId());
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery().startedBy(flowableUserExpand.getCurrentUserId());

        return operateProcessList(processInstanceQuery, historicProcessInstanceQuery, queryVo);
    }

    @Override
    public FlowablePageDto<FlowableTaskDto> todoTaskList(FlowableTaskQueryVo queryVo) {

        int startIndex = (queryVo.getPageIndex() - 1) * queryVo.getPageSize();
        int endIndex = queryVo.getPageIndex() * queryVo.getPageSize();

        TaskQuery query = taskService.createTaskQuery()
                .taskAssignee(flowableUserExpand.getCurrentUserId());

        if(queryVo.getProcessDefineKey() != null){
            query.processDefinitionKey(queryVo.getProcessDefineKey());
        }

        if(queryVo.getProcessInstanceId() != null){
            query.processDefinitionId(queryVo.getProcessInstanceId());
        }

        List<Task> list = query.orderByTaskCreateTime().desc().listPage(startIndex, endIndex);
        //总量
        long count = query.count();
        List<FlowableTaskDto> res = new ArrayList<>();

        //补充流程信息
        if(list != null && !list.isEmpty()){
            Map<String, ProcessInstance> processMap = runtimeService.createProcessInstanceQuery()
                    .processInstanceIds(list.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet()))
                    .list()
                    .stream()
                    .collect(
                            Collectors.toMap(
                                    Execution::getProcessInstanceId,
                                    processInstance -> processInstance
                            )
                    );

            //处理结果
            res = list.stream().map(task -> {
                FlowableTaskDto ftd = FlowableTaskDto.builder()
                        .id(task.getId())
                        .name(task.getName())
                        .receiveTime(task.getCreateTime())
                        .build();

                ProcessInstance processInstance = processMap.get(task.getProcessInstanceId());
                if(processInstance != null){
                    ftd.setFlowableProcess( FlowableProcessDto.builder()
                            .starter(processInstance.getStartUserId())
                            .processInstanceId(processInstance.getProcessInstanceId())
                            .processDefinitionName(processInstance.getProcessDefinitionName())
                            .build()
                    );
                }
                return ftd;
            }).collect(Collectors.toList());
        }

        return new FlowablePageDto<>(queryVo.getPageIndex(), queryVo.getPageIndex(), (int)count, res);
    }

    @Override
    public FlowablePageDto<FlowableTaskDto> approvedTaskList(FlowableTaskQueryVo queryVo){

        int startIndex = (queryVo.getPageIndex() - 1) * queryVo.getPageSize();
        int endIndex = queryVo.getPageIndex() * queryVo.getPageSize();

        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(flowableUserExpand.getCurrentUserId())  //指定办理人
                .finished();// 已完成的任务

        if(queryVo.getProcessDefineKey() != null){
            query.processDefinitionKey(queryVo.getProcessDefineKey());
        }

        if(queryVo.getProcessInstanceId() != null){
            query.processDefinitionId(queryVo.getProcessInstanceId());
        }

        //查询
        List<HistoricTaskInstance> list = query
                .orderByHistoricTaskInstanceEndTime().desc()    //按完成时间倒序
                .listPage(startIndex, endIndex);    //分页

        //总量
        long count = query.count();
        List<FlowableTaskDto> res = new ArrayList<>();

        //补充流程信息
        if(list != null && !list.isEmpty()){
            Map<String, ProcessInstance> processMap = runtimeService.createProcessInstanceQuery()
                    .processInstanceIds(list.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet()))
                    .list()
                    .stream()
                    .collect(
                            Collectors.toMap(
                                    Execution::getProcessInstanceId,
                                    processInstance -> processInstance
                            )
                    );

            //处理结果
            res = list.stream().map(task -> {
                FlowableTaskDto ftd = FlowableTaskDto.builder()
                        .id(task.getId())
                        .name(task.getName())
                        .receiveTime(task.getCreateTime())
                        .operateTime(task.getEndTime())
                        .build();

                ProcessInstance processInstance = processMap.get(task.getProcessInstanceId());
                if(processInstance != null){
                    ftd.setFlowableProcess( FlowableProcessDto.builder()
                            .starter(processInstance.getStartUserId())
                            .processInstanceId(processInstance.getProcessInstanceId())
                            .processDefinitionName(processInstance.getProcessDefinitionName())
                            .build()
                    );
                }
                return ftd;
            }).collect(Collectors.toList());
        }

        return new FlowablePageDto<>(queryVo.getPageIndex(), queryVo.getPageIndex(), (int)count, res);
    }

    @Override
    public Map<String, Object> variables(String processId) {
        return runtimeService.getVariables(processId);
    }

    @Override
    public List<FlowableTaskDto> records(String processId) {

        //验证当前流程
        HistoricProcessInstance hi = validateHistoryProcessInstance(processId);

        //获取流程所有历史节点
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId)
                .activityTypes(new HashSet<>(Arrays.asList(FlowableConstant.ACT_TYPE_START_EVENT, FlowableConstant.ACT_TYPE_USER_TASK, FlowableConstant.ACT_TYPE_END_EVENT)))
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        //获取所有评论，并按照是否有 taskId 来分组
        Map<Boolean, List<Comment>> commentMap = taskService.getProcessInstanceComments(processId)
                .stream().collect(Collectors.partitioningBy(comment -> Objects.isNull(comment.getTaskId())));

        Map<String, String> taskMap = commentMap.getOrDefault(
                false,
                new ArrayList<>()
        ).stream().collect(Collectors.toMap(
                Comment::getTaskId,
                Comment::getFullMessage
        ));

        List<FlowableTaskDto> res = list.stream().map(e -> FlowableTaskDto.builder()
                .id(e.getTaskId())
                .name(e.getActivityName())
                .receiveTime(e.getStartTime())
                .operateTime(e.getEndTime())
                .userId(e.getAssignee())
                .comment(((Function<HistoricActivityInstance, String>) e1 -> {

                    switch (e1.getActivityType()) {
                        case FlowableConstant.ACT_TYPE_END_EVENT:
                            Optional<Comment> endComment = commentMap.get(true).stream().filter(comment -> comment.getType().equals(FlowableConstant.COMMENT_TYPE_STOP)).findFirst();
                            if (endComment.isPresent()) {
                                return endComment.get().getFullMessage();
                            } else {
                                return "流程结束";
                            }
                        case FlowableConstant.ACT_TYPE_START_EVENT:
                            Optional<Comment> startComment = commentMap.get(false).stream().filter(comment -> comment.getType().equals(FlowableConstant.COMMENT_TYPE_START)).findFirst();
                            if (startComment.isPresent()) {
                                return startComment.get().getFullMessage();
                            } else {
                                return "流程开始";
                            }
                        case FlowableConstant.ACT_TYPE_USER_TASK:
                            //用户节点
                            return taskMap.get(e1.getTaskId());
                        default:
                            return null;
                    }
                }).apply(e))
                .build()).collect(Collectors.toList());

        //如果流程是被删除（取消的），需要手动补上结束节点
        if(hi.getDeleteReason() != null){

            //查询结束节点
            String suitableEndEvent = findSuitableEndEvent(hi.getProcessDefinitionId());

            //目前不知道是被谁删除的，可能需要特殊处理
            res.add(
                    FlowableTaskDto.builder()
                            .name(suitableEndEvent)
                            .comment(hi.getDeleteReason())
                            .receiveTime(hi.getEndTime())
                            .build()
            );
        }
        return res;
    }

    @Override
    public byte[] processDiagram(String processInstanceId) throws IOException {

        // 1. 获取 BPMN 模型
        BpmnModel bpmnModel = getBpmnModel(processInstanceId);

        // 2. 当前活动节点
        List<String> currentActivityIds = getCurrentActivities(processInstanceId);

        // 3. 获取执行过的连线
        List<String> highLightedFlowIds = getExecutedFlows( processInstanceId);

        // 4. 生成图表
        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        try (InputStream imageStream = diagramGenerator.generateDiagram(
                bpmnModel,                          // BPMN 模型
                "png",                              // 图片格式
                currentActivityIds,              // 高亮的活动节点（当前节点）
                highLightedFlowIds,                   // 高亮的连线
                "宋体",                             // 活动节点字体
                "宋体",                             // 标签字体
                "宋体",                             // 注释字体
                null,                               // ClassLoader
                1.0,                                // 缩放比例
                true                                // 是否生成默认图
        ) ) {
            return IOUtils.toByteArray(imageStream);
        }
    }

    /**
     * 获取BPMN模型
     */
    private BpmnModel getBpmnModel(String processInstanceId) {
        // 先检查流程实例是否在运行
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        String processDefinitionId;
        if (processInstance != null) {
            processDefinitionId = processInstance.getProcessDefinitionId();
        } else {
            // 流程已结束，从历史记录获取
            HistoricProcessInstance historicInstance = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            processDefinitionId = historicInstance.getProcessDefinitionId();
        }

        return repositoryService.getBpmnModel(processDefinitionId);
    }

    /**
     * 获取当前活动节点
     */
    private List<String> getCurrentActivities(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if (processInstance != null) {
            return runtimeService.getActiveActivityIds(processInstanceId);
        }

        return Collections.emptyList(); // 流程已结束，无当前节点
    }

    /**
     * 获取已执行的连线
     */
    private List<String> getExecutedFlows(String processInstanceId) {
        List<String> executedFlows = new ArrayList<>();

        try {
            // 直接查询历史活动实例，过滤出 sequenceFlow 类型的活动
            List<HistoricActivityInstance> flowActivities = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .activityType("sequenceFlow")
                    .list();


            for (HistoricActivityInstance flowActivity : flowActivities) {
                executedFlows.add(flowActivity.getActivityId());
            }

        } catch (Exception e) {
            System.err.println("查询连线历史失败: " + e.getMessage());
        }

        return executedFlows;
    }

    /**
     * 查找合适的结束节点
     * @param processDefinitionId 流程定义 id
     * */
    private String findSuitableEndEvent(String processDefinitionId){
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process mainProcess = bpmnModel.getMainProcess();
        
        //查找所有的结束事件
        List<EndEvent> endEvents = mainProcess.findFlowElementsOfType(EndEvent.class);
        if(endEvents.isEmpty()){
            throw new FlowableCustomException("流程定义 " + processDefinitionId + " 未找到结束节点");
        }

        //尽可能找没有附加事件的普通节点
        Optional<EndEvent> normalNode = endEvents.stream()
                .filter(e -> e.getEventDefinitions().isEmpty())
                .findFirst();

        if(normalNode.isPresent()){
            return normalNode.get().getId();
        }
        return endEvents.get(0).getId();
    }

    /**
     * 验证历史流程
     * */
    private HistoricProcessInstance validateHistoryProcessInstance(String processInstanceId){
        if(processInstanceId == null){
            throw new FlowableCustomException("流程 id 参数不能为 null");
        }

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if(historicProcessInstance == null ){
            throw new FlowableCustomException("流程实例不存在");
        }
        return historicProcessInstance;
    }

    /**
     * 验证执行中的流程流程
     */
    private ProcessInstance validateRunTimeProcessInstance(String processInstanceId){
        if(processInstanceId == null){
            throw new FlowableCustomException("流程 id 参数不能为 null");
        }

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if(processInstance == null){
            throw new FlowableCustomException("流程实例不存在或已结束");
        }

        if(processInstance.isSuspended()){
            throw new FlowableCustomException("流程挂起中，无法操作");
        }
        return processInstance;
    }
}
